﻿//在C#中比较两个对象是否相等，可以使用以下几种方法：

//使用Equals方法：C#中的所有对象都继承自Object类，而Object类中定义了Equals方法用于比较两个对象是否相等。
//    默认情况下，Equals方法比较的是对象的引用是否相等，即是否指向同一个内存地址。
//    如果需要比较对象的值是否相等，可以在自定义类中重写Equals方法。
//例如，假设有一个名为Person的类，可以在该类中重写Equals方法来比较两个Person对象的属性是否相等：


//public class Person
//{
//    public string Name { get; set; }
//    public int Age { get; set; }

//    public override bool Equals(object obj)
//    {
//        if (obj == null || GetType() != obj.GetType())
//        {
//            return false;
//        }

//        Person other = (Person)obj;
//        return Name == other.Name && Age == other.Age;
//    }
//}
//然后可以使用Equals方法来比较两个Person对象是否相等：


//Person person1 = new Person { Name = "Alice", Age = 25 };
//Person person2 = new Person { Name = "Alice", Age = 25 };

//bool isEqual = person1.Equals(person2); // 返回true
//使用==运算符：在C#中，可以使用==运算符来比较两个对象是否相等。对于引用类型，==运算符比较的是对象的引用是否相等；
//    对于值类型，==运算符比较的是对象的值是否相等。
//例如，对于int类型的对象，可以直接使用==运算符来比较：



//csharp
//插入代码
//复制代码
//int a = 10;
//int b = 10;

//bool isEqual = (a == b); // 返回true
//使用EqualityComparer类：C#提供了EqualityComparer类来比较两个对象是否相等。该类提供了静态的Equals方法，可以用于比较两个对象是否相等。
//例如，可以使用EqualityComparer类来比较两个字符串是否相等：



//csharp
//插入代码
//复制代码
//string str1 = "Hello";
//string str2 = "Hello";

//bool isEqual = EqualityComparer<string>.Default.Equals(str1, str2); // 返回true
//以上是在C#中比较两个对象是否相等的几种常见方法。根据具体的需求和对象类型，可以选择适合的方法来进行比较。

//模式：查看字符计算器

//11月28日 08:36 会话ID:(MOSS_1701063692579)

//C# System.Object.ReferenceEquals 和 equals的区别
//11月28日 08:36 会话ID:(MOSS_1701063692579)

//在C#中，System.Object.ReferenceEquals和Equals方法都用于比较两个对象是否相等，但它们有一些区别：

//ReferenceEquals方法是一个静态方法，用于比较两个对象的引用是否相等。它不会调用对象的Equals方法，而是直接比较对象的引用。如果两个对象引用同一个内存地址，则返回true；否则返回false。
//例如：



//csharp
//插入代码
//复制代码
//string str1 = "Hello";
//string str2 = "Hello";
//string str3 = str1;

//bool isEqual1 = ReferenceEquals(str1, str2); // 返回false
//bool isEqual2 = ReferenceEquals(str1, str3); // 返回true
//Equals方法是一个实例方法，用于比较两个对象的值是否相等。
//    默认情况下，Equals方法比较的是对象的引用，即是否指向同一个内存地址。
//    但是，可以在自定义类中重写Equals方法，来改变比较的行为。
//例如：



//csharp
//插入代码
//复制代码
//public class Person
//{
//    public string Name { get; set; }
//    public int Age { get; set; }

//    public override bool Equals(object obj)
//    {
//        if (obj == null || GetType() != obj.GetType())
//        {
//            return false;
//        }

//        Person other = (Person)obj;
//        return Name == other.Name && Age == other.Age;
//    }
//}

//Person person1 = new Person { Name = "Alice", Age = 25 };
//Person person2 = new Person { Name = "Alice", Age = 25 };

//bool isEqual = person1.Equals(person2); // 返回true
//需要注意的是，如果没有重写Equals方法，那么默认情况下，Equals方法和ReferenceEquals方法的行为是一样的，都是比较对象的引用是否相等。

//总结来说，ReferenceEquals方法比较的是对象的引用是否相等，而Equals方法比较的是对象的值是否相等。在自定义类中，
//可以重写Equals方法来改变比较的行为

using System;
using System.Text;

//public class program
//{

//    static void Main(string[] args)
//    {

        //System.String a1 = "a";
        //System.String a2 = "a";

        //System.Console.WriteLine(a1 == a2); // true 值相等性
        //System.Console.WriteLine(a1.Equals(a2));// true  值相等性  对于引用类型可以自定义类中重写Equals
        //System.Console.WriteLine(System.Object.ReferenceEquals(a1, a2));// true 引用也相等
        //System.Console.ReadKey();

        //System.String a3 = new System.String("a".ToCharArray());
        //System.String a4 = new System.String("a".ToCharArray());

        //System.Console.WriteLine(a3 == a4);  // true 值相等性
        //System.Console.WriteLine(a3.Equals(a4));// true    值相等性   对于引用类型可以自定义类中重写Equals

        //System.Console.WriteLine(System.Object.ReferenceEquals(a3, a4));// false  引用不相等
        //System.Console.ReadKey();


        //    在这段代码中，a1和a2都是string类型的变量，它们分别被赋值为相同的字符串字面量 "a"。

        //     当我们使用 == 运算符比较两个引用类型时，它比较的是两个引用是否指向同一个对象。
        //    在这种情况下，a1和a2都指向相同的字符串字面量 "a"，因此它们指向同一个对象。

        //    所以，System.Console.WriteLine(a1 == a2) 的结果将会是 true，因为它们指向同一个字符串对象。

        // 需要注意的是，这种行为是由于字符串的常量折叠（string interning）机制导致的。
        //        在编译时，编译器会对相同的字符串字面量进行优化，将它们指向同一个字符串对象，
        //        以节省内存。因此，当我们使用相同的字符串字面量赋值给不同的变量时，它们实际上会指向同一个对象。

        //然而，如果我们使用 new 关键字显式地创建字符串对象，即使它们的内容相同，它们也不会指向同一个对象。例如：


        //                System.String a1 = new System.String("a".ToCharArray());
        //                        System.String a2 = new System.String("a".ToCharArray());

        //                        System.Console.WriteLine(a1 == a2);  // 输出 false
        //                        在这种情况下，a1和a2是两个不同的字符串对象，因此 a1 == a2 的结果将会是 false。 这里实际结果却是true ？？？？？？？？

//    }

//}

//----------------------------------------------如何为类或结构定义值相等性（C# 编程指南）
//----------------------------------------------类示例
//----------------------------------------------下面的示例演示如何在类（引用类型）中实现值相等性。
//class TwoDPoint : IEquatable<TwoDPoint>
//{
//    public int X { get; private set; }
//    public int Y { get; private set; }

//    public TwoDPoint(int x, int y)
//    {
//        if (  (x < 1 || x> 2000) || (y < 1 || y> 2000))
//        {
//            throw new ArgumentException("Point must be in range 1 - 2000");
//        }
//        this.X = x;
//        this.Y = y;
//    }

//    public override bool Equals(object obj) => this.Equals(obj as TwoDPoint);

//    public bool Equals(TwoDPoint p)
//    {
//        if (p is null)
//        {
//            return false;
//        }

//        // Optimization for a common success case.
//        if (Object.ReferenceEquals(this, p))
//        {
//            return true;
//        }

//        // If run-time types are not exactly the same, return false.
//        if (this.GetType() != p.GetType())
//        {
//            return false;
//        }

//        // Return true if the fields match.
//        // Note that the base class is not invoked because it is
//        // System.Object, which defines Equals as reference equality.
//        return (X == p.X) && (Y == p.Y);
//    }


//    public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
//    {
//        if (lhs is null)
//        {
//            if (rhs is null)
//            {
//                return true;
//            }

//            // Only the left side is null.
//            return false;
//        }
//        // Equals handles case of null on right side.
//        return lhs.Equals(rhs);
//    }

//    public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
//}

//// For the sake of simplicity, assume a ThreeDPoint IS a TwoDPoint.
//class ThreeDPoint : TwoDPoint, IEquatable<ThreeDPoint>
//{
//    public int Z { get; private set; }

//    public ThreeDPoint(int x, int y, int z)
//        : base(x, y)
//    {
//        if ((z < 1) || (z > 2000))
//        {
//            throw new ArgumentException("Point must be in range 1 - 2000");
//        }
//        this.Z = z;
//    }

//    public override bool Equals(object obj) => this.Equals(obj as ThreeDPoint);

//    public bool Equals(ThreeDPoint p)
//    {
//        if (p is null)
//        {
//            return false;
//        }

//        // Optimization for a common success case.
//        if (Object.ReferenceEquals(this, p))
//        {
//            return true;
//        }

//        // Check properties that this class declares.
//        if (Z == p.Z)
//        {
//            // Let base class check its own fields
//            // and do the run-time type comparison.
//            return base.Equals((TwoDPoint)p);
//        }
//        else
//        {
//            return false;
//        }
//    }


//    public static bool operator ==(ThreeDPoint lhs, ThreeDPoint rhs)
//    {
//        if (lhs is null)
//        {
//            if (rhs is null)
//            {
//                // null == null = true.
//                return true;
//            }

//            // Only the left side is null.
//            return false;
//        }
//        // Equals handles the case of null on right side.
//        return lhs.Equals(rhs);
//    }

//    public static bool operator !=(ThreeDPoint lhs, ThreeDPoint rhs) => !(lhs == rhs);
//}

//class Program
//{
//    static void Main(string[] args)
//    {
//        ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
//        ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
//        ThreeDPoint pointC = null;
//        int i = 5;

//        Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
//        Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
//        Console.WriteLine("null comparison = {0}", pointA.Equals(pointC));
//        Console.WriteLine("Compare to some other type = {0}", pointA.Equals(i));

//        TwoDPoint pointD = null;
//        TwoDPoint pointE = null;

//        Console.WriteLine("Two null TwoDPoints are equal: {0}", pointD == pointE);

//        pointE = new TwoDPoint(3, 4);
//        Console.WriteLine("(pointE == pointA) = {0}", pointE == pointA);
//        Console.WriteLine("(pointA == pointE) = {0}", pointA == pointE);
//        Console.WriteLine("(pointA != pointE) = {0}", pointA != pointE);

//        //System.Collections.ArrayList list = new System.Collections.ArrayList();
//        //list.Add(new ThreeDPoint(3, 4, 5));
//        //Console.WriteLine("pointE.Equals(list[0]): {0}", pointE.Equals(list[0]));

//        TwoDPoint p1 = new ThreeDPoint(1, 2, 3);
//        TwoDPoint p2 = new ThreeDPoint(1, 2, 4);
//        Console.WriteLine(p1.Equals(p2)); // output: True
//        //根据此代码报告，尽管 z 值有所不同，但 p1 等于 p2。
//        //    由于编译器会根据编译时类型选取 IEquatable 的 TwoDPoint 实现，因而会忽略该差异。

//        // Keep the console window open in debug mode.
//        Console.WriteLine("Press any key to exit.");
//        Console.ReadKey();
//    }
//}

/* Output:
    pointA.Equals(pointB) = True
    pointA == pointB = True
    null comparison = False
    Compare to some other type = False
    Two null TwoDPoints are equal: True
    (pointE == pointA) = False
    (pointA == pointE) = False
    (pointA != pointE) = True
    pointE.Equals(list[0]): False
*/


//public class  Student{

//    private string name;
//    private int age;
//    public Student(string name, int age) {
//        this.name = name;
//        this.age = age;
//    }
//}
//class program {


//    public static void Main(string[] args) {

//        Student stu1 = new Student("szq", 11);
//        Student stu2 = new Student("szq", 11);

//        Console.WriteLine(stu1==stu2); //           --> false
//        Console.WriteLine(stu1.Equals(stu2));//           --> false
//        Console.WriteLine(System.Object.ReferenceEquals(stu1,stu2));//           --> false
//        Console.WriteLine("Press any key to exit.");
//        Console.ReadKey();

//    }
//}



////----------------------------------------------如何为类或结构定义值相等性（C# 编程指南）
////----------------------------------------------结构示例
////----------------------------------------------下面的示例演示如何在结构（值类型）中实现值相等性：

//namespace ValueEqualityStruct
//{
//    struct TwoDPoint : IEquatable<TwoDPoint>
//    {
//        public int X { get; private set; }
//        public int Y { get; private set; }

//        public TwoDPoint(int x, int y)
//            : this()
//        {
//            if (  (x < 1 || x> 2000) ||   (y < 1 || y> 2000))
//            {
//                throw new ArgumentException("Point must be in range 1 - 2000");
//            }
//            X = x;
//            Y = y;
//        }

//        public override bool Equals(object obj) => obj is TwoDPoint other && this.Equals(other);

//        public bool Equals(TwoDPoint p) => X == p.X && Y == p.Y;

//        //public override int GetHashCode() => (X, Y).GetHashCode();

//        //对于结构，Object.Equals(Object)（System.ValueType 中的替代版本）的默认实现通过使用反射来比较类型中每个字段的值，
//        //从而执行值相等性检查。 实施者替代结构中的 Equals 虚方法时，
//        //目的是提供更高效的方法来执行值相等性检查，并选择根据结构字段或属性的某个子集来进行比较。
//        //除非结构显式重载了 == 和 != 运算符，否则这些运算符无法对结构进行运算。

//        public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs) => lhs.Equals(rhs);

//        public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
//    }

//    class Program
//    {
//        static void Main(string[] args)
//        {
//            TwoDPoint pointA = new TwoDPoint(3, 4);
//            TwoDPoint pointB = new TwoDPoint(3, 4);
//            int i = 5;

//            // True:
//            Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
//            // True:
//            Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
//            // True:
//            Console.WriteLine("object.Equals(pointA, pointB) = {0}", object.Equals(pointA, pointB));
//            // False:
//            Console.WriteLine("pointA.Equals(null) = {0}", pointA.Equals(null));
//            // False:
//            Console.WriteLine("(pointA == null) = {0}", pointA == null);
//            // True:
//            Console.WriteLine("(pointA != null) = {0}", pointA != null);
//            // False:
//            Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
//            // CS0019:
//            // Console.WriteLine("pointA == i = {0}", pointA == i);

//            //// Compare unboxed to boxed.
//            //System.Collections.ArrayList list = new System.Collections.ArrayList();
//            //list.Add(new TwoDPoint(3, 4));
//            //// True:
//            //Console.WriteLine("pointA.Equals(list[0]): {0}", pointA.Equals(list[0]));

//            // Compare nullable to nullable and to non-nullable.
//            TwoDPoint? pointC = null;
//            TwoDPoint? pointD = null;
//            // False:
//            Console.WriteLine("pointA == (pointC = null) = {0}", pointA == pointC);
//            // True:
//            Console.WriteLine("pointC == pointD = {0}", pointC == pointD);

//            TwoDPoint temp = new TwoDPoint(3, 4);
//            pointC = temp;
//            // True:
//            Console.WriteLine("pointA == (pointC = 3,4) = {0}", pointA == pointC);

//            pointD = temp;
//            // True:
//            Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD == pointC);

//            Console.WriteLine("Press any key to exit.");
//            Console.ReadKey();
//        }
//    }

//    /* Output:
//        pointA.Equals(pointB) = True
//        pointA == pointB = True
//        Object.Equals(pointA, pointB) = True
//        pointA.Equals(null) = False
//        (pointA == null) = False
//        (pointA != null) = True
//        pointA.Equals(i) = False
//        pointE.Equals(list[0]): True
//        pointA == (pointC = null) = False
//        pointC == pointD = True
//        pointA == (pointC = 3,4) = True
//        pointD == (pointC = 3,4) = True
//    */
//}

//----------------------------------------------如何测试引用相等性（标识）（C# 编程指南）
//----------------------------------------------



namespace TestReferenceEquality
{
    struct TestStruct
    {
        public int Num { get; private set; }
        public string Name { get; private set; }

        public TestStruct(int i, string s) : this()
        {
            Num = i;
            Name = s;
        }
    }

    class TestClass
    {
        public int Num { get; set; }
        public string Name { get; set; }
    }

    class Program03
    {
        //static void Main()
        //{
        //    // Demonstrate reference equality with reference types.
        //    #region ReferenceTypes

        //    // Create two reference type instances that have identical values.
        //    TestClass tcA = new TestClass() { Num = 1, Name = "New TestClass" };
        //    TestClass tcB = new TestClass() { Num = 1, Name = "New TestClass" };

        //    Console.WriteLine("ReferenceEquals(tcA, tcB) = {0}",
        //                        Object.ReferenceEquals(tcA, tcB)); // false

        //    // After assignment, tcB and tcA refer to the same object.
        //    // They now have reference equality.
        //    tcB = tcA;
        //    Console.WriteLine("After assignment: ReferenceEquals(tcA, tcB) = {0}",
        //                        Object.ReferenceEquals(tcA, tcB)); // true

        //    // Changes made to tcA are reflected in tcB. Therefore, objects
        //    // that have reference equality also have value equality.
        //    tcA.Num = 42;
        //    tcA.Name = "TestClass 42";
        //    Console.WriteLine("tcB.Name = {0} tcB.Num: {1}", tcB.Name, tcB.Num);
        //    #endregion

        //    // Demonstrate that two value type instances never have reference equality.
        //    #region ValueTypes

        //    TestStruct tsC = new TestStruct(1, "TestStruct 1");

        //    // Value types are copied on assignment. tsD and tsC have
        //    // the same values but are not the same object.
        //    TestStruct tsD = tsC;
        //    Console.WriteLine("After assignment: ReferenceEquals(tsC, tsD) = {0}",
        //                        Object.ReferenceEquals(tsC, tsD)); // false
        //    #endregion

        //    #region stringRefEquality
        //    // Constant strings within the same assembly are always interned by the runtime.
        //    // This means they are stored in the same location in memory. Therefore,
        //    // the two strings have reference equality although no assignment takes place.
        //    string strA = "Hello world!";
        //    string strB = "Hello world!";
        //    Console.WriteLine("ReferenceEquals(strA, strB) = {0}",
        //                     Object.ReferenceEquals(strA, strB)); // true

        //    // After a new string is assigned to strA, strA and strB
        //    // are no longer interned and no longer have reference equality.
        //    strA = "Goodbye world!";
        //    Console.WriteLine("strA = \"{0}\" strB = \"{1}\"", strA, strB);

        //    Console.WriteLine("After strA changes, ReferenceEquals(strA, strB) = {0}",
        //                    Object.ReferenceEquals(strA, strB)); // false

        //    // A string that is created at runtime cannot be interned.
        //    StringBuilder sb = new StringBuilder("Hello world!");
        //    string stringC = sb.ToString();
        //    // False:
        //    Console.WriteLine("ReferenceEquals(stringC, strB) = {0}",
        //                    Object.ReferenceEquals(stringC, strB));

        //    // The string class overloads the == operator to perform an equality comparison.
        //    Console.WriteLine("stringC == strB = {0}", stringC == strB); // true

        //    #endregion

        //    // Keep the console open in debug mode.
        //    Console.WriteLine("Press any key to exit.");
        //    Console.ReadKey();
        //}
    }
}

/* Output:
    ReferenceEquals(tcA, tcB) = False
    After assignment: ReferenceEquals(tcA, tcB) = True
    tcB.Name = TestClass 42 tcB.Num: 42
    After assignment: ReferenceEquals(tsC, tsD) = False
    ReferenceEquals(strA, strB) = True
    strA = "Goodbye world!" strB = "Hello world!"
    After strA changes, ReferenceEquals(strA, strB) = False
    ReferenceEquals(stringC, strB) = False
    stringC == strB = True
*/
